﻿using FastBuild.Init.TimeJob;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using SqlSugar;
using System.Xml.Serialization;
using Wechat_PublicNumber.Common;
using Wechat_PublicNumber.Entity;
using Wechat_PublicNumber.Model;
using Wechat_PublicNumber.Model.Input;
using Wechat_PublicNumber.Repository;
using Wechat_PublicNumber.SettingModel;

namespace Wechat_PublicNumber.Controllers
{
    [Route("Wx")]
    [ApiController]
    public class WxController : BasicController
    {
        #region DI
        [Autowired]
        private WxRepository _wxRepsitory;

        [Autowired]
        private WxHttpInvoke _wxHttpInvoke;

        [Autowired]
        private CommonHttpInvoke _commonHttpInvoke;

        [Autowired]
        private WxTemplateRepository _templateRepository;

        [Autowired]
        private WxChatRepository _chatRepository;

        [Autowired]
        private WxSetting _wxSetting;

        [Autowired]
        private IJwtHelper _jwtHelper;

        [Autowired]
        private WechatAccessToken _wechatAccessToken;

        [Autowired]
        private DomainSetting _domainSetting;

        [Autowired]
        private ITimeJobService _jobService;

        #endregion

        /// <summary>
        /// ReplyUserActionTest
        /// </summary>
        /// <returns></returns>
        [HttpGet("/WxTest/ReplyUserAction"), AllowAnonymous]
        public async Task<string> ReplyUserActionTest([FromQuery] string keyword)
        {
            return await _chatRepository.ReplyUserAction(keyword, "otCQI6q07YZUqxc06iOqkQVcqeYw");
        }

        /// <summary>
        /// CustomSendTest
        /// </summary>
        /// <param name="openID"></param>
        /// <param name="parm"></param>
        /// <param name="type"></param>
        /// <returns></returns>
        [HttpGet("/WxTest/CustomSend")]
        public async Task CustomSendTest([FromQuery] string openID, string parm, string type)
        {
            switch (type)
            {
                case "image":
                    await _wxHttpInvoke.CustomSend(new CustomSendRequest() { touser = openID, msgtype = "image", image = new CustomSendRequest_image() { media_id = parm } });
                    break;
                case "text":
                    await _wxHttpInvoke.CustomSend(new CustomSendRequest() { touser = openID, msgtype = "text", text = new CustomSendRequest_text() { content = parm } });
                    break;
                default:
                    break;
            }
        }

        /// <summary>
        /// GetWeChatToken
        /// </summary>
        /// <returns></returns>
        [HttpGet("/WxTest/GetWeChatToken")]
        public string GetWeChatToken() => _wechatAccessToken.access_token;

        /// <summary>
        /// 微信对接校验绑定
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        [HttpGet("Wechat"), AllowAnonymous, DefaultReturn]
        public string WxBinding([FromQuery] WeChatSignModel model)
        {
            try
            {
                _logger.Info($"WxBinding_QueryString：{Request.QueryString.Value}");
                if (_wxRepsitory.CheckWxBind(model))
                {
                    _logger.Info("WxBinding Success");
                    return model.Echostr;
                }
                else
                {
                    _logger.Info("WxBinding failure");
                    return "校验失败";
                }
            }
            catch (Exception ex)
            {
                _logger.Error(ex, "WxBinding ERROR");
                throw;
            }
        }

        /// <summary>
        /// 事件接收
        /// </summary>
        /// <returns></returns>
        [HttpPost("Wechat"), AllowAnonymous, DefaultReturn]
        public async Task<string> WxXMLReceive()
        {
            var getData = HttpContext.Request.Query;
            var getDataDic = getData.Keys.ToDictionary(k => k, k => getData[k].ToString());
            WeChatSignModel signModel = DicToEntityHelper.Assign<WeChatSignModel>(getDataDic);

            var request = HttpContext.Request;
            StreamReader sr = new StreamReader(request.Body);
            string body = await sr.ReadToEndAsync();
            object xmlResult = null;
            var xmlBaseResult = Deserialize<WeChatXMLBaseModel>(body, out xmlResult);
            var returnMessage = "success";

            switch (xmlBaseResult.MsgType)
            {
                //微信推送系统类消息
                case "event":
                    {
                        WeChatEventEnum value;
                        if (Enum.TryParse(xmlBaseResult.Event, out value))
                        {
                            switch (Enum.Parse<WeChatEventEnum>(xmlBaseResult.Event))
                            {
                                //地理位置 用来获取用户所在城市 存库
                                case WeChatEventEnum.LOCATION:
                                    {
                                        var model = Deserialize<WeChatLocationModel>(body, out xmlResult);
                                        await SaveCity((WeChatLocationModel)xmlResult);
                                    }
                                    break;
                                //用户点击菜单
                                case WeChatEventEnum.CLICK:
                                    {
                                        var model = Deserialize<WeChatClickMenuModel>(body, out xmlResult);
                                        returnMessage = await _chatRepository.ReplyUserMessage("HQMB", model.FromUserName);
                                    }
                                    break;
                                //关注订阅公众号
                                case WeChatEventEnum.subscribe:
                                    {
                                        var model = Deserialize<WeChatClickMenuModel>(body, out xmlResult);
                                        if (model.EventKey.StartsWith("qrscene_FollowWeChat"))
                                            returnMessage = _chatRepository.GetReplyMessageXML(model.FromUserName, "关注成功！点击链接跳转首页！", "欢迎加入！！", $"{_domainSetting.WebDomain}/File/Image/FollowWeChat.png", $"{_domainSetting.WebDomain}?Follow=true");
                                    }
                                    break;
                                //二维码扫描
                                case WeChatEventEnum.SCAN:
                                    {
                                        var model = Deserialize<WeChatClickMenuModel>(body, out xmlResult);
                                        if (model.EventKey == "FollowWeChat")
                                            returnMessage = _chatRepository.GetReplyMessageXML(model.FromUserName, "点击链接跳转首页！", "欢迎加入！！", $"{_domainSetting.WebDomain}/File/Image/FollowWeChat.png", $"{_domainSetting.WebDomain}?Follow=true");
                                    }
                                    break;
                                default:
                                    break;
                            }
                        }
                    }
                    break;
                //用户发送文本消息
                case "text":
                    {
                        var model = Deserialize<WeChatTextModel>(body, out xmlResult);
                        returnMessage = await _chatRepository.ReplyUserMessage(model.Content, model.FromUserName);
                    }
                    break;
                default:
                    break;
            }

            _logger.Info($"接收微信通知:\nsignModel:{JsonConvert.SerializeObject(signModel)};" +
                                   $"\nresultModel:{body};" +
                                   $"\nJson:{(xmlResult is not null ? JsonConvert.SerializeObject(xmlResult) : "")}" +
                                   $"\nResult:{returnMessage}");

            return returnMessage;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="redirectUrl"></param>
        /// <param name="code"></param>
        /// <param name="state"></param>
        /// <returns></returns>
        [HttpGet("/WxLogin"), AllowAnonymous]
        public async Task<IActionResult> WxLogin(string redirectUrl, string code, string state)
        {
            try
            {
                if (!_wxSetting.IS_WxLogin)
                {
                    var u = await _wxRepsitory.GetUserInfoByOpenID(_wxSetting.AdminOpenID);

                    return Content(getWechatLoginHtml(redirectUrl, u.ID), contentType: "text/html; charset=utf-8");
                }

                //正常调用
                if (string.IsNullOrEmpty(code))
                    return Redirect(GetWeChatAuthorizationUrl(redirectUrl, "snsapi_base"));

                //微信回调
                var now = DateTime.Now;
                var accessToken = await _wxHttpInvoke.AccessToken_Web(_wxSetting.AppID, _wxSetting.AppSecret, code);
                if (accessToken == null)
                    throw new Exception("Get Token Error");

                var webToken = await _wxRepsitory.GetUserWebAccessTokenByOpenID(accessToken.openid);

                if (state == "snsapi_base")
                {
                    _logger.Debug($"snsapi_base begin", "WeChatLogin");
                    //获取token数据 如果没有或者上次获取时间到现在大于30天就重新获取
                    if (webToken is null || (webToken.UpdateTime == null ? webToken.CreateTime : webToken.UpdateTime.Value).AddDays(30) <= now)
                        return Redirect(GetWeChatAuthorizationUrl(redirectUrl, "snsapi_userinfo"));

                    _logger.Debug($"snsapi_base end", "WeChatLogin");
                }
                else if (state == "snsapi_userinfo")
                {
                    _logger.Debug($"snsapi_userinfo begin", "WeChatLogin");
                    if (webToken is null)
                    {
                        webToken = new UserWebAccessToken()
                        {
                            OpenID = accessToken.openid,
                            CreateTime = now
                        };
                    }
                    else webToken.UpdateTime = now;

                    webToken.RefreshToken = accessToken.refresh_token;

                    await _wxRepsitory.SaveUserWebAccessToken(webToken);

                    _logger.Debug($"snsapi_userinfo end", "WeChatLogin");
                }

                var userInfo = await _wxHttpInvoke.UserInfo_Web(accessToken.access_token, webToken.OpenID);
                if (userInfo == null)
                {
                    //刷新token
                    var webToken_New = await _wxHttpInvoke.RefreshToken_Web(_wxSetting.AppID, webToken.RefreshToken);
                    if (webToken_New is null)
                        return Redirect(GetWeChatAuthorizationUrl(redirectUrl, "snsapi_userinfo"));

                    webToken.RefreshToken = webToken_New.refresh_token;
                    webToken.UpdateTime = now;
                    await _wxRepsitory.SaveUserWebAccessToken(webToken);
                }

                UserInfo user = null;

                user = await _wxRepsitory.GetUserInfoByOpenID(webToken.OpenID);
                if (user is null)
                {
                    user = new UserInfo()
                    {
                        OpenID = webToken.OpenID,
                        CreateTime = now
                    };
                }
                else
                    user.UpdateTime = now;

                user.Sex = userInfo.sex;
                user.Headimgurl = userInfo.headimgurl;
                user.NickName = userInfo.nickname;

                user.ID = await _wxRepsitory.SaveUserInfo(user);

                _logger.Debug($"SaveEntityInfo User", "WeChatLogin");

                return Content(getWechatLoginHtml(redirectUrl, user.ID), contentType: "text/html; charset=utf-8");
            }
            catch (Exception ex)
            {
                _logger.Error(ex, "WxLogin Error");
                throw;
            }
        }

        /// <summary>
        /// 向公众号所有用户推送微信模板数据
        /// </summary>
        /// <returns></returns>
        [HttpPost("PushTemp")]
        public async Task WxPushTemp([FromBody] WxPushTempInput input)
        {
            try
            {
                var openIdList = await _wxHttpInvoke.UserOpenIdList();
                var pushData = await _templateRepository.GetTemplateData(input.TemplateId, openIdList.data.openid, input.Parm);
                if (pushData is not null) await _wxHttpInvoke.PushTemplate(pushData);
            }
            catch (Exception ex)
            {
                _logger.Error(ex, "PushTemp Error");
                throw;
            }
        }

        /// <summary>
        /// 根据OpenId向公众号用户推送微信模板数据
        /// </summary>
        /// <returns></returns>
        [HttpPost("PushTemp/OpenId")]
        public async Task WxPushTempByOpenId([FromBody] WxPushTempByOpenIdInput input)
        {
            try
            {
                var pushData = await _templateRepository.GetTemplateData(input.TemplateId, input.OpenId, input.Parm);
                if (pushData is not null) await _wxHttpInvoke.PushTemplate(pushData);
            }
            catch (Exception ex)
            {
                _logger.Error(ex, "PushTemp Error");
                throw;
            }
        }

        /// <summary>
        /// 根据Remark向公众号用户推送微信模板数据
        /// </summary>
        /// <returns></returns>
        [HttpPost("PushTemp/Remark")]
        public async Task WxPushTempByRemark([FromBody] WxPushTempByRemarkInput input)
        {
            try
            {
                var userInfo = (await _wxHttpInvoke.UserList()).FirstOrDefault(s => s.remark == input.Remark);
                if (userInfo is null) throw new OutPutException("未找到该用户！！");

                var pushData = await _templateRepository.GetTemplateData(input.TemplateId, userInfo.openid, input.Parm);
                if (pushData is not null) await _wxHttpInvoke.PushTemplate(pushData);
            }
            catch (Exception ex)
            {
                _logger.Error(ex, "PushTemp Error");
                throw;
            }
        }

        /// <summary>
        /// 修改公众号备注
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        [HttpPatch("User/Remark")]
        public async Task WxUpdateRemarkToUser([FromBody] WxUpdateRemarkToUserRequest model)
        {
            try
            {
                await _wxHttpInvoke.UpdateRemarkToUser(model);
            }
            catch (Exception ex)
            {
                _logger.Error(ex, "UpdateRemarkToUser Error");
                throw;
            }
        }

        /// <summary>
        /// 获取公众号用户信息
        /// </summary>
        /// <param name="openId"></param>
        /// <returns></returns>
        [HttpGet("User/{openId}")]
        public async Task<WxUserInfoRespone> WxUserInfo([FromRoute] string openId)
        {
            try
            {
                return await _wxHttpInvoke.UserInfo(openId);
            }
            catch (Exception ex)
            {
                _logger.Error(ex, "UserInfo Error");
                throw;
            }

        }

        /// <summary>
        /// 获取公众号所有用户信息
        /// </summary>
        /// <returns></returns>
        [HttpGet("User")]
        public async Task<List<WxUserInfoRespone>> WxUserList()
        {
            try
            {
                return await _wxHttpInvoke.UserList();
            }
            catch (Exception ex)
            {
                _logger.Error(ex, "UserList Error");
                throw;
            }
        }

        /// <summary>
        /// 获取公众号所有用户的地址信息
        /// </summary>
        /// <returns></returns>
        [HttpGet("User/Address")]
        public async Task<List<UserAddressRespone>> UserAddress()
        {
            try
            {
                var userInfo = await _wxHttpInvoke.UserList();

                var userAddress = await _wxRepsitory.GetUserInfoByOpenIDs(userInfo.Select(s => s.openid).ToList());

                return userInfo.Select(s =>
                {
                    var oneAddress = userAddress.Where(a => a.OpenID == s.openid).FirstOrDefault();
                    return new UserAddressRespone
                    {
                        Remark = s.remark,
                        Province = oneAddress?.Province,
                        City = oneAddress?.City,
                        Area = oneAddress?.Area,
                        TownShip = oneAddress?.TownShip,
                    };
                }).ToList();
            }
            catch (Exception ex)
            {
                _logger.Error(ex, "UserAddress Error");
                throw;
            }
        }

        /// <summary>
        /// 更新用户地址
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        [HttpPost("User/Address")]
        public async Task SaveUserAddress([FromBody] WeChatLocationModel model)
        {
            await SaveCity(model);
        }

        #region private
        /// <summary>
        /// 微信专用
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="xml"></param>
        /// <param name="outData"></param>
        /// <returns></returns>
        private T Deserialize<T>(string xml, out object outData)
        {
            XmlSerializer xmlSerializer = new XmlSerializer(typeof(T), new XmlRootAttribute("xml"));
            StringReader stringReader1 = new StringReader(xml);
            StringReader stringReader2 = stringReader1;
            T obj = (T)xmlSerializer.Deserialize(stringReader2);
            stringReader1.Close();
            stringReader1.Dispose();
            outData = obj;
            return obj;
        }

        /// <summary>
        /// 保存城市地区
        /// </summary>
        /// <param name="model"></param>
        private async Task SaveCity(WeChatLocationModel model)
        {
            var now = DateTime.Now;

            var userInfo = await _wxRepsitory.GetUserInfoByOpenID(model.FromUserName);
            if (userInfo is null)
            {
                userInfo = new UserInfo()
                {
                    OpenID = model.FromUserName,
                    CreateTime = now
                };
            }
            else
            {
                //6个小时执行一次位置信息的更新
                if (userInfo.UpdateTime.HasValue && userInfo.UpdateTime.Value > now.AddHours(-6))
                    return;

                userInfo.UpdateTime = now;
            }

            var jwd = await _commonHttpInvoke.GetAddressByLongitude_Latitude(model.Longitude, model.Latitude);

            if (jwd is not null)
            {
                var cityData = jwd.regeocode.addressComponent;
                userInfo.Province = cityData.province;
                userInfo.City = cityData.city.ToString() == "[]" ? cityData.province : cityData.city.ToString();
                userInfo.CityCode = cityData.citycode;
                userInfo.Area = cityData.district;
                userInfo.AreaCode = cityData.adcode;
                userInfo.TownShip = cityData.township;
                userInfo.Address = jwd.regeocode.formatted_address;

                await _wxRepsitory.SaveUserInfo(userInfo);
            }
        }

        /// <summary>
        /// 获取微信网页端认证Url
        /// </summary>
        /// <param name="redirectUrl"></param>
        /// <param name="scope"></param>
        /// <returns></returns>
        private string GetWeChatAuthorizationUrl(string redirectUrl, string scope)
        {
            var thisUrl = System.Net.WebUtility.UrlEncode($"{(Request.IsHttps ? "https" : "http")}://{Request.Host}{Request.PathBase.Value}{Request.Path.Value}?redirectUrl={redirectUrl}");
            _logger.Debug($"thisUrl:{thisUrl}", "WeChatLogin");

            var returnUrl = $"https://open.weixin.qq.com/connect/oauth2/authorize?appid={_wxSetting.AppID}&redirect_uri={thisUrl}&response_type=code&scope={scope}&state={scope}#wechat_redirect";
            _logger.Debug($"returnUrl:{returnUrl}", "WeChatLogin");

            return returnUrl;
        }

        /// <summary>
        /// 获取微信登录的Html
        /// </summary>
        /// <param name="redirectUrl"></param>
        /// <param name="userID"></param>
        /// <returns></returns>
        private string getWechatLoginHtml(string redirectUrl, long userID)
        {
            var returnData = $@"
<html>
  <head>
    <script>
      localStorage.removeItem('Token');
      localStorage.setItem('Token','{_jwtHelper.Encrypt(new ToeknData() { UserID = userID })}');
      window.location.href = '{redirectUrl}'
    </script>
  </head>
</html>";
            _logger.Debug($"getWechatLoginHtml:{returnData}", "WeChatLogin");
            return returnData;
        }
        #endregion
    }
}
